#!/usr/bin/env python
"""Kant Generator for Python

Generates mock philosophy based on a context-free grammar

Usage: python kgp.py [options] [source]

Options:
    -g ..., --grammar=...   use specified grammar file or URL
    -h, --help              show this help
    -d                      show debugging information while parsing

Examples:
kgp.py                  generates several paragraphs of Kantian philosophy
kgp.py -g husserl.xml   generates several paragraphs of Husserl
kpg.py "<xref id='paragraph'/>"  generates a paragraph of Kant
kgp.py template.xml     reads from template.xml to decide what to generate
"""
from xml.dom import minidom
import random
import toolbox
import sys
import getopt

_debug = 0


class NoSourceError(Exception): pass

class KantGenerator:
    """ generates mock philosophy based on a context-free grammar"""


    def __init__(self, grammar, source=None):
        self.loadGrammar(grammar)
        self.loadSource(source and source or self.getDefaultSource())
        self.refresh()


    def _load(self, source):
        """ Load XML input source, return parsed XML document


        - a URL of a remote XML file ("http://diveintopython.org/kant.xml")
        - a file name of a local XML file
        - a standard input("-")
        """

        sock = toolbox.openAnything(source)
        print sock
        xmldoc = minidom.parse(sock).documentElement
        sock.close()
        return xmldoc


    def loadGrammar(self, grammar):
        """load context-free grammar"""
        self.grammar = self._load(grammar)
        self.refs = {}
        for ref in self.grammar.getElementsByTagName("ref"):
            self.refs[ref.attributes["id"].value] = ref


    def loadSource(self, source):
        """load source"""
        self.source = self._load(source)


    def getDefaultSource(self):
        """ guess default source of the current grammar

        The default source will be one of the <ref>s that is not
        cross-referenced. 
        """
        xrefs = {}
        for xref in self.grammar.getElementsByTagName("xref"):
            xrefs[xref.attributes["id"].value] = 1
        xrefs = xrefs.keys()
        standaloneXrefs = [e for e in self.refs.keys() if e not in xrefs]
        if not standaloneXrefs:
            raise NoSourceError, "can't guess source, and no source specified"
        return '<xref id="%s"/>' % random.choice(standaloneXrefs)

    def reset(self):
        self.pieces = []
        self.capitallizeNextWord = 0

    def refresh(self):
        """reset output buffer, re-parse entire source file, and return output"""

        self.reset()
        self.parse(self.source)
        return self.output()

    def output(self):
        return "".join(self.pieces)


    def randomChildElement(self, node):
        """choose a random child element of a node"""

        choices  = [ e for e in node.childNodes
                if e.nodeType == e.ELEMENT_NODE ]
        chosen = random.choice(choices)

        if _debug:
            sys.stderr.write("%s available choices: %s\n" % \
                    (len(choise), [ e.toxml() for e in choises]))
            sys.stderr.write('Chosen: %s\n' % chosen.toxml())
        return chosen

    def parse(self, node):
        parseMethod = getattr(self,"parse_%s" % node.__class__.__name__)
        parseMethod(node)

    def parse_Document(self, node):
        self.parse(node.documentElement)

    def parse_Text(self, node):
        text = node.data
        if self.capitallizeNextWord:
            self.pieces.append(text[0].upper())
            self.pieces.append(text[1:])
            self.capitallizeNextWord = 0
        else:
            self.pieces.append(text)

    def parse_Element(self, node):
        handlerMethod = getattr(self, "do_%s" % node.tagName)
        handlerMethod(node)

    def parse_Comment(self, node):
        pass

    def do_xref(self, node):
        id = node.attributes["id"].value
        self.parse(self.randomChildElement(self.refs[id]))
        pass

    def do_p(self, node):
        keys = node.attributes.keys()
        if "class" in keys:
            if node.attributes["class"].value == "sentence":
                self.capitallizeNextWord = 1
        if "chance" in keys:
            chance =int(node.attributes["chance"].value)
            doit = (chance > random.randrange(100))
        else:
            doit = 1
        if doit:
            for child in node.childNodes: self.parse(child)

    def do_choice(self, node):
        self.parse(self.randomChildElement(node))

def usage():
    print __doc__

def main(argv):
    grammar = "kant.xml"

    try:
        opts, args = getopt.getopt(argv, "hg:d", ["help", "grammar="])
    except:
        usage()
        sys.exit()
    for opt,arg in opts:
        if opt in ("-h", "--help"):
            usage()
            sys.exit()
        elif opt == "-d":
            global _debug
            _debug = 1
        elif opt in ("-g", "--grammar"):
            grammar = arg
    source = "".join(args)

    k = KantGenerator(grammar, source)
    print k.output()

if __name__ == "__main__":
    main(sys.argv[1:])



